package e.odbo.data.dao;

import com.openbravo.data.basic.BasicException;
import com.openbravo.data.loader.I_Session;
import e.odbo.data.bean.key.KeyBuilder;
import e.odbo.data.dao.interceptor.AutoGeneratorUUIDInterceptor;
import e.odbo.data.dao.interceptor.CreateDateInterceptor;
import e.odbo.data.dao.interceptor.ModifiedLogInterceptor;
import e.odbo.data.dsl.query.QBFParameter;
import e.odbo.data.dsl.query.QBFParameters;
import e.odbo.data.dao.table.I_TableSentenceBuilder;
import e.odbo.data.dao.table.TableDefinition;
import e.odbo.data.bean.IKeyed;
import e.odbo.data.bean.I_BaseBean;
import e.odbo.data.bean.I_ViewBean;
import e.odbo.data.sample.security.I_SecurityFilter;
import e.odbo.data.dsl.Select;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public class EntityManager {
    public static final Logger logger= LoggerFactory.getLogger(EntityManager.class);

    protected Map<Class, BaseDAO> daoMap = new HashMap<Class, BaseDAO>();
    protected Map<Class, InfoDAO> infoDaoMap = new HashMap<Class, InfoDAO>();
    protected Map<Class, ViewDAO> viewDaoMap = new HashMap<Class, ViewDAO>();

    private I_TableSentenceBuilder builder;
    protected I_SecurityFilter securityFilter;

    private HashMap<Class,Class> transParentClassHashMap=new HashMap<>();

    private I_Session session;

    protected List<I_DAOInterceptor> interceptorList=new ArrayList<>();

    public void addDAOInterceptor(I_DAOInterceptor interceptor){
        interceptorList.add(interceptor);
    }

    public void removeDAOInterceptor(I_DAOInterceptor interceptor){
        interceptorList.remove(interceptor);
    }

    public I_DAOInterceptor[] getDAOInterceptor(Class beanClass){
        if(interceptorList.isEmpty())
            return null;
        List<I_DAOInterceptor> daoInterceptors=new ArrayList<>();
        for(I_DAOInterceptor interceptor:interceptorList)
            if(interceptor.interceptorAble(beanClass))
                daoInterceptors.add(interceptor);
        return daoInterceptors.toArray(new I_DAOInterceptor[daoInterceptors.size()]);
    }

    public EntityManager(I_Session session,I_TableSentenceBuilder builder){
        this.session=session;
        this.builder=builder;
        this.securityFilter=builder.getSecurityFilter();
        addDefaultInterceptor();
    }

    private void addDefaultInterceptor(){
        interceptorList.add(new CreateDateInterceptor());
        interceptorList.add(new ModifiedLogInterceptor());
        interceptorList.add(new AutoGeneratorUUIDInterceptor());
    }

    public void addDaoToMap(ViewDAO obj) {
        if (obj instanceof BaseDAO<?>) {
            BaseDAO baseDAO=(BaseDAO) obj;
            baseDAO.init(builder,getDAOInterceptor(baseDAO.getSuportClass()));
            daoMap.put(obj.getSuportClass(),  baseDAO);
        }else if (obj instanceof InfoDAO)
            infoDaoMap.put(obj.getSuportClass(), (InfoDAO) obj);
        else
            viewDaoMap.put(obj.getSuportClass(), obj);
    }

    public ViewDAO getDao(Class c) {
        ViewDAO d = viewDaoMap.get(c);
        if (d == null)
            d = infoDaoMap.get(c);
        if (d == null)
            d = daoMap.get(c);
        return d;
    }

    public int insert(I_BaseBean obj) throws BasicException {
        BaseDAO dao = daoMap.get(obj.getClass());
        if (dao == null)
            throw new BasicException("DAO no suport insert:" + obj.getClass());
        return dao.insert(obj);
    }

    public IKeyed insertAutoGenerator(I_BaseBean obj) throws BasicException {
        insert(obj);
        return KeyBuilder.getBeanKey(obj);
    }

    public void setSecurityFilter(I_SecurityFilter securityFilter) {
        this.securityFilter = securityFilter;
    }

    public int update(I_BaseBean obj) throws BasicException {
        BaseDAO dao =daoMap.get(obj.getClass());
        if (dao == null)
            throw new BasicException("DAO no suport update:" + obj.getClass());
        return dao.update(obj);
    }

    public int delete(I_BaseBean obj) throws BasicException {
        BaseDAO dao = daoMap.get(obj.getClass());
        if (dao == null)
            throw new BasicException("DAO no suport delete:" + obj.getClass());
        return dao.delete(obj);
    }

    public int deleteObjs(List<I_BaseBean> objs) throws BasicException {
        BaseDAO dao = daoMap.get(objs.get(0).getClass());
        if (dao == null)
            throw new BasicException("DAO no suport list delete:" + objs.get(0).getClass());
        return dao.delete(objs);
    }

    public int insertObjs(List objs) throws BasicException {
        if (objs.isEmpty())
            return 1;
        BaseDAO dao = daoMap.get(objs.get(0).getClass());
        if (dao == null)
            throw new BasicException("DAO no suport list insert:" + objs.get(0).getClass());
        return dao.insert(objs);
    }

    public int updateObjs(List objs) throws BasicException {
        if (objs.isEmpty())
            return 1;
        BaseDAO dao = daoMap.get(objs.get(0).getClass());
        if (dao == null)
            throw new BasicException("DAO no suport list update:" + objs.get(0).getClass());
        return dao.update(objs);
    }

    public int insertOrUpdate(List objs) throws BasicException {
        if (objs.isEmpty())
            return 1;
        BaseDAO dao = daoMap.get(objs.get(0).getClass());
        if (dao == null)
            throw new BasicException("DAO no suport list update:" + objs.get(0).getClass());
        return dao.insertOrUpdate(objs);
    }


    public int count(Class c,QBFParameters filter) throws BasicException {
        ViewDAO dao = getDao(c);
        if (dao == null)
            throw new BasicException("DAO no suport count:" + c);
        return dao.count(filter);
    }

    public int count(Class c) throws BasicException {
       ViewDAO dao=getDao(c);
        if (dao == null)
            throw new BasicException("DAO no suport count:" + c);
        return dao.count();
    }


    public int insertOrUpdate(I_BaseBean obj) throws BasicException {
        BaseDAO dao = daoMap.get(obj.getClass());
        if (dao == null)
            throw new BasicException("DAO no suport update:" + obj.getClass());

         if (obj.getKey() == null || !dao.exist(obj))
                return insert(obj);

        return update(obj);
    }

    public int deleteByIKeyed(Class c, IKeyed k) throws BasicException {
        BaseDAO dao = daoMap.get(c);
        if (dao == null)
            throw new BasicException("DAO no suport delete:" + c);
        return dao.delete(k);
    }


    public int deleteByQBFParameters(Class c, QBFParameters filter) throws BasicException {
        BaseDAO dao =daoMap.get(c);
        if (dao == null)
            throw new BasicException("DAO no suport list delete:" + c);
        return dao.delete(filter);
    }

    public I_ViewBean find(Class c, IKeyed id) throws BasicException {
        if (daoMap.containsKey(c))
            return (I_ViewBean) daoMap.get(c).find(id);
        else if (infoDaoMap.containsKey(c))
            return (I_ViewBean) infoDaoMap.get(c).find(id);
        throw new BasicException("DAO no suport find:" + c);
    }

    public I_ViewBean findByStringId(Class c, String id) throws BasicException {
        return find(c, KeyBuilder.getKey(id));
    }

    public I_ViewBean findByIntId(Class c, int id) throws BasicException {
        return find(c, KeyBuilder.getKey(id));
    }

    public I_BaseBean find(Class c, QBFParameters filter)throws BasicException{
        BaseDAO dao = daoMap.get(c);
        if (dao == null)
            throw new BasicException("DAO no suport find by QBFParameters:" + c);
        return (I_BaseBean)dao.find(filter);
    }

    public List list(Class c) throws BasicException {
        ViewDAO dao=getDao(c);
        if(dao==null)
           throw new BasicException("DAO no suport list:" + c);
        return dao.list();
    }

    public List query(Class c, QBFParameters filter) throws BasicException {
        ViewDAO dao=getDao(c);
        if(dao==null)
            throw new BasicException("DAO no suport query by filters:" + c);
        return dao.query(filter);
    }

    public List queryByQBFParameter(Class c, QBFParameter filter) throws BasicException {
        ViewDAO dao=getDao(c);
        if(dao==null)
            throw new BasicException("DAO no suport query by filter:" + c);
        return dao.query(filter);
    }

    public List listByParent(Class c, final I_ParentNode parent) throws BasicException {
        if (daoMap.containsKey(c))
            return daoMap.get(c).list(new I_ParentNode<String>() {
                public IKeyed<String> getParentKey() {
                    return parent.getParentKey();
                }
                public Class getParentClass() {
                    Class c=transParentClassHashMap.get(parent.getParentClass());
                    if(c==null)
                        c=parent.getParentClass();
                    return c;
                }
            });
        throw new BasicException("DAO no suport list:" + c);
    }

    public List list(Class c, final Class parent, final String id) throws BasicException {
        return listByParent(c, new I_ParentNode<String>() {
            public IKeyed<String> getParentKey() {
                return KeyBuilder.getKey(id);
            }

            public Class getParentClass() {
                return parent;
            }
        });
    }

    public int updateByFieldWithIKeyed(IKeyed obj, String field, Object newValue) throws BasicException {
        BaseDAO dao = daoMap.get(obj.getClass());
        if (dao == null)
            throw new BasicException("DAO no suport updateByField:" + obj.getClass());
        return dao.updateByField(obj, field, newValue);
    }


    public int updateByField(Class c, IKeyed obj, String field, Object newValue) throws BasicException {
        BaseDAO dao = daoMap.get(c);
        if (dao == null)
            throw new BasicException("DAO no suport updateByField:" + obj.getClass());
        return dao.updateByField(obj, field, newValue);
    }

    public TableDefinition getTableDefinition(Class c) {
        BaseDAO dao = daoMap.get(c);
        if (dao != null)
            return dao.getTable();
        return null;
    }


    public int clear(Class c) throws BasicException {
        BaseDAO dao = daoMap.get(c);
        if (dao == null)
            throw new BasicException("DAO no suport delete:" + c);
        return dao.clear();
    }


    public List listWithClassName(String c) throws BasicException {
        Class cs;
        try {
            cs = Class.forName(c);
        } catch (ClassNotFoundException e) {
            throw new BasicException("DAO no suport updateByField:" + c);
        }
        return list(cs);
    }

    public List listWithParentClassAndKey(String c, String parent, String id)
            throws BasicException {
        Class cs, parent1;
        try {
            cs = Class.forName(c);
            parent1 = Class.forName(parent);
        } catch (ClassNotFoundException e) {
            throw new BasicException("DAO no suport updateByField:" + c);
        }
        return list(cs, parent1, id);
    }

    public List queryByQBFParameters(String c, QBFParameters filter) throws BasicException {
        Class cs;
        try {
            cs = Class.forName(c);
        } catch (ClassNotFoundException e) {
            throw new BasicException("DAO no suport updateByField:" + c);
        }
        return query(cs, filter);
    }

    public List listWithDateTime(Class c, Date start, Date end)
            throws BasicException {
        I_DataTimeListSuport listDao = null;
        if (daoMap.containsKey(c)) {
            BaseDAO dao = daoMap.get(c);
            if (dao instanceof I_DataTimeListSuport)
                listDao = (I_DataTimeListSuport) dao;
            else
                return dao.list();
        } else if (infoDaoMap.containsKey(c)) {
            InfoDAO dao = infoDaoMap.get(c);
            if (dao instanceof I_DataTimeListSuport)
                listDao = (I_DataTimeListSuport) dao;
            else
                return dao.list();
        } else if (viewDaoMap.containsKey(c)) {
            ViewDAO dao = viewDaoMap.get(c);
            if (dao instanceof I_DataTimeListSuport)
                listDao = (I_DataTimeListSuport) dao;
            else
                return dao.list();
        }
        if (listDao == null)
            throw new BasicException("DAO no suport operation:" + c);
        return listDao.listWithTime(start, end);
    }


    public List listWithDateTimeByClassName(String c, Date start, Date end)
            throws BasicException {
        Class cs;
        try {
            cs = Class.forName(c);
        } catch (ClassNotFoundException e) {
            throw new BasicException("DAO no suport updateByField:" + c);
        }
        return listWithDateTime(cs, start, end);
    }


    public boolean isExistByField(Class c, String field, Object value)
            throws BasicException {
        BaseDAO dao = daoMap.get(c);
        if (dao == null)
            throw new BasicException("DAO no suport delete:" + c);
        return dao.isExistByField(field, value);
    }

    public boolean isExistByFields(Class c, String[] fields, Object[] values)
            throws BasicException {
        BaseDAO dao = daoMap.get(c);
        if (dao == null)
            throw new BasicException("DAO no suport delete:" + c);
        return dao.isExistByFields(fields, values);
    }

    public boolean isExist(Class c,QBFParameters parameters) throws BasicException {
        BaseDAO dao = daoMap.get(c);
        if (dao == null)
            throw new BasicException("DAO no suport delete:" + c);
        return dao.isExist(parameters);
    }

    public void setTransParentClass(Class t,Class v){
        this.transParentClassHashMap.put(t,v);
    }

    public I_TableSentenceBuilder getBuilder() {
        return builder;
    }

    public I_Session getSession() {
        return session;
    }

    public List listBySelect(Class c, Select select) throws BasicException {
        ViewDAO listDao=getDao(c);
        if (listDao == null)
            throw new BasicException("DAO no suport operation:" + c);
        return listDao.listBySelect(select);
    }
}
